﻿//  Copyright (c) 2011 Ray Liang (http://www.dotnetage.com)
//  Licensed MIT: http://www.opensource.org/licenses/mit-license.php

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DNA.Mvc.Data.Entity
{
    public class WebPageRepository : EntityRepository<WebPage>, IWebPageRepository
    {
        public WebPageRepository() : base() { }

        public WebPageRepository(DB dbContext) : base(dbContext) { }

        /// <summary>
        /// Delete the page by specified page id
        /// </summary>
        /// <param name="id">The web page id.</param>
        public virtual void Delete(int id)
        {
            if (id <= 0) throw new ArgumentOutOfRangeException("id");

            this.Context.Database.ExecuteSqlCommand(string.Format("DELETE FROM dna_WebPages WHERE ID={0}", id));
        }

        /// <summary>
        /// Find web page by virual path
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public virtual WebPage Find(string path)
        {
            if (string.IsNullOrEmpty(path)) return null;

            return this.DbSet.SqlQuery(string.Format("SELECT * FROM dna_WebPages WHERE [Path]=N'{0}'", path.ToLower())).SingleOrDefault();
        }

        public virtual string[] GetRoles(int id)
        {
            if (id <= 0) throw new ArgumentOutOfRangeException("id");
            return Context.WebPageRoles.Where(r => r.PageID == id).Select(p => p.RoleName).ToArray();
        }

        public virtual IEnumerable<WebPage> GetChildren(int webID, int parentID)
        {
            if (parentID < 0) throw new ArgumentOutOfRangeException("parentID");

            return this.DbSet.AsNoTracking().Where(p => p.ParentID == parentID && p.WebID == webID).ToList();
        }

        public virtual  bool IsExists(string path)
        {
            if (string.IsNullOrEmpty(path)) return false;
            return DbSet.Count(p => p.Path.Equals(path, StringComparison.OrdinalIgnoreCase)) > 0;
        }

        private void Reindex(IEnumerable<WebPage> webpages, int parentID)
        {
            var pages = webpages.Where(p => p.ParentID == parentID).ToList();
            for (int i = 0; i < pages.Count; i++)
                pages[i].Pos = i;
        }

        public virtual void Move(int parentID, int id, int pos)
        {
            var page = Find(id);

            if (page == null)
                throw new WebPageNotFoundException(id.ToString());

            var webPages = DbSet.Where(p => p.WebID == page.WebID);
            var seqCollection = webPages.Where(p => p.ParentID == parentID)
                                                .OrderBy(p => p.Pos)
                                                .Select(p => p.Pos);

            if (seqCollection.Count() == 0)
            {
                page.Pos = 0;
                page.ParentID = parentID;
                if (IsOwnContext)
                    Context.SaveChanges();
                return;
            }

            int upperBound = seqCollection.Max();
            int lowerBound = seqCollection.Min();

            int _from = page.Pos;
            int _to = pos;

            if (_to > upperBound)
                _to = upperBound;
            else
            {
                if (_to < lowerBound)
                    _to = lowerBound;
            }

            //1.Move up
            if (_from > _to)
            {
                webPages.Where(p => p.ParentID == parentID && p.Pos >= _to && p.Pos < _from)
                    .OrderBy(p => pos)
                    .AsParallel()
                    .ForAll(p => p.Pos++);

            }

            //2.Move down
            if (_from < _to)
            {
                webPages.Where(p => p.ParentID == parentID && p.Pos > _from && p.Pos <= _to)
                   .OrderBy(p => pos)
                   .AsParallel()
                   .ForAll(p => p.Pos--);
            }

            page.Pos = _to;
            page.ParentID = parentID;

            if (IsOwnContext) Context.SaveChanges();
        }

        public override void Delete(WebPage t)
        {
            if (t == null) throw new ArgumentNullException("t");

            if (t.ID <= 0) throw new ArgumentOutOfRangeException("Web.ID");

            Context.Database.ExecuteSqlCommand(string.Format("UPDATE dna_WebPages SET ParentID={0} WHERE ParentID={1}", t.ParentID, t.ID));
            //Context.
            base.Delete(t);
        }

        public override WebPage Create(WebPage t)
        {
            if (t.Web == null)
                throw new ArgumentNullException("WebPage.Web");

            if (string.IsNullOrEmpty(t.Path))
                throw new ArgumentNullException("WebPage.Path");

            if (string.IsNullOrEmpty(t.Owner))
                throw new ArgumentNullException("WebPage.Owner");

            if (this.IsExists(t.Path))
                throw new WebPageIsExistsException(t.Path);

            if (string.IsNullOrEmpty(t.Target))
                t.Target = "_self";

            if ((t.Created == null) || (t.Created.Equals(DateTime.MinValue)))
                t.Created = DateTime.UtcNow;

            if ((t.LastModified == null) || (t.LastModified.Equals(DateTime.MinValue)))
                t.LastModified = DateTime.UtcNow;

            if (t.Pos == 0)
            {
                var seq= DbSet.Where(p => p.ParentID == t.ParentID && p.WebID == t.Web.Id).OrderBy(p => p.Pos).Select(p=>p.Pos);
                if (seq.Count() > 0)
                    t.Pos = seq.Max() + 1;
            }

            return base.Create(t);
        }

        public virtual void AddRoles(WebPage page, string[] roles)
        {
            if (page == null) throw new ArgumentNullException("page");

            if (page.ID <= 0) throw new WebPageNotFoundException();

            if ((roles == null) || (roles.Length == 0)) throw new ArgumentNullException("roles");

            foreach (var role in roles)
                Context.WebPageRoles.Add(new WebPageRole() { WebPage = page, RoleName = role });

            if (page.AllowAnonymous)
                page.AllowAnonymous = false;

            if (IsOwnContext)
                Context.SaveChanges();
        }

        public virtual void AddRoles(int id, string[] roles)
        {
            if (id == 0) throw new ArgumentOutOfRangeException("id");
            if ((roles == null) || (roles.Length == 0)) throw new ArgumentNullException("roles");
            var page = this.Find(id);
            this.AddRoles(page, roles);
        }

        public virtual void ClearRoles(int id)
        {
            if (id == 0) throw new ArgumentOutOfRangeException("id");
            var rows = Context.Database.ExecuteSqlCommand(string.Format("DELETE FROM dna_WebPageRoles WHERE PageID={0}", id));
            var page = Find(id);
            page.AllowAnonymous = true;
            Update(page);
        }

        public virtual void SetToDefault(int webID, int pageID)
        {
            var web = Context.Webs.Find(webID);
            var page = Context.WebPages.Find(pageID);
            web.DefaultUrl = page.Path;
            if (IsOwnContext)
                Context.SaveChanges();
        }

        public virtual IEnumerable<WebPageRole> GetRoles(int[] ids)
        {
            return Context.WebPageRoles.Where(w => ids.Contains(w.PageID)).ToList();
        }

        public virtual WebPage Create(Web web, int parentID, WebPageTemplate template)
        {
            if (template == null)
                throw new ArgumentNullException("template");

            var newPage = new WebPage()
            {
                Title = template.Title,
                Description = template.Description,
                Keywords=template.Keywords,
                ImageUrl=template.ImageUrl,
                IconUrl=template.IconUrl,
                LinkUrl = template.LinkUrl,
                Target = template.Target,
                ViewData = template.Data,
                ViewName = template.Layout,
                AllowAnonymous = template.AllowAnonymous,
                ShowInMenu = template.ShowInMenu,
                IsShared = template.IsShared,
                IsStatic = template.IsStatic,
                Path = template.Path,
                ParentID = parentID,
                Owner = web.Owner,
                Web = web
            };

            if (template.Path.IndexOf("{website}") > -1)
                newPage.Path = template.Path.ToLower().Replace("{website}", web.Name.ToLower());

            if (!template.IsStatic && string.IsNullOrEmpty(template.Layout) && template.Path.StartsWith("~/sites/"))
                newPage.ViewName = "Layout_1";

            return this.Create(newPage);
        }
    }
}
